package wallet

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/zeromicro/go-zero/core/stores/cache"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
	"navi-mumbai/common/gensql"
	"navi-mumbai/common/key/walletkey"
	"time"
)

var _ UserDepositFlowModel = (*customUserDepositFlowModel)(nil)

type (
	// UserDepositFlowModel is an interface to be customized, add more methods here,
	// and implement the added methods in customUserDepositFlowModel.
	UserDepositFlowModel interface {
		userDepositFlowModel
		InsertTrans(ctx context.Context, tx sqlx.Session, data *UserDepositFlow) (sql.Result, error)
		UpdateState(ctx context.Context, flowNo int64, state int64, exFlow string, errMsg string, gwFlowNo int64) error
		UpdateTrans(ctx context.Context, tx sqlx.Session, flowNo int64, state int64, exFlow string, gwFlowNo int64) error
		Find(ctx context.Context, uid int64, state []int64, moneyType int64, currencyType int64, payChannelId int64, start, end *time.Time, page, pageSize int64) ([]*UserDepositFlow, error)
		Count(ctx context.Context, uid int64, state []int64, moneyType int64, currencyType int64, payChannelId int64, start, end *time.Time) (int64, error)
		SumAmount(ctx context.Context, payChannelId int64, start, end *time.Time) (int64, error)
		CountSuccessUser(ctx context.Context, payChannelId int64, start, end *time.Time) (int64, error)
		CountSuccessUser2(ctx context.Context, start, end *time.Time) (int64, error)
		CountSuccessFlow(ctx context.Context, payChannelId int64, start, end *time.Time) (int64, error)
		CountNewUserCount(ctx context.Context, start, end *time.Time, startUid, endUid int64) (int64, error)
		SumNewUserAmount(ctx context.Context, start, end *time.Time, startUid, endUid int64) (int64, error)
	}

	customUserDepositFlowModel struct {
		*defaultUserDepositFlowModel
		genSql *gensql.GenSql
	}
)

// NewUserDepositFlowModel returns a model for the database table.
func NewUserDepositFlowModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) UserDepositFlowModel {
	return &customUserDepositFlowModel{
		defaultUserDepositFlowModel: newUserDepositFlowModel(conn, c, opts...),
		genSql:                      gensql.NewGenSql(),
	}
}

func (m *customUserDepositFlowModel) InsertTrans(ctx context.Context, tx sqlx.Session, data *UserDepositFlow) (sql.Result, error) {
	query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, userDepositFlowRowsExpectAutoSet)
	ret, err := tx.ExecCtx(ctx, query, data.FlowNo, data.Uid, data.MoneyType, data.MoneyAmount, data.CurrencyType, data.CurrencyAmount, data.State, data.ExternalFlowNo, data.PayChannelId, data.PayAccount, data.ErrMsg, data.PayAccountType, data.GwFlowNo)
	return ret, err
}

func (m *customUserDepositFlowModel) UpdateTrans(ctx context.Context, tx sqlx.Session, flowNo int64, state int64, exFlow string, gwFlowNo int64) error {
	query := fmt.Sprintf("update %s set state = ?, external_flow_no = ?, gw_flow_no = ? where flow_no = ?", m.table)
	_, err := tx.ExecCtx(ctx, query, state, exFlow, gwFlowNo, flowNo)
	if err != nil {
		return err
	}
	return nil
}

func (m *customUserDepositFlowModel) Find(ctx context.Context, uid int64, state []int64, moneyType int64, currencyType int64, payChannelId int64, start, end *time.Time, page, pageSize int64) ([]*UserDepositFlow, error) {
	query := fmt.Sprintf("select %s from %s ", userDepositFlowRows, m.table)
	m.genSql.InitSql(query)

	if uid != 0 {
		m.genSql = m.genSql.Where("uid = ?", uid)
	}
	if len(state) != 0 {
		if len(state) > 1 {
			m.genSql = m.genSql.Where("state in (?)", state)
		} else {
			m.genSql = m.genSql.Where("state = ?", state[0])
		}
	}
	if moneyType != 0 {
		m.genSql = m.genSql.Where("money_type = ?", moneyType)
	}
	if currencyType != 0 {
		m.genSql = m.genSql.Where("currency_type = ?", currencyType)
	}
	if payChannelId != 0 {
		m.genSql = m.genSql.Where("pay_channel_id = ?", payChannelId)
	} else {
		m.genSql = m.genSql.Where("pay_channel_id not in (?, ?)", walletkey.PayChannelIdTest, walletkey.PayChannelIdSystem)
	}
	if start != nil {
		m.genSql = m.genSql.Where("create_time >= ?", start)
	}
	if end != nil {
		m.genSql = m.genSql.Where("create_time <= ?", end)
	}
	// 分页
	if page < 1 {
		page = 1
	}
	if pageSize <= 0 {
		pageSize = 10
	}

	q, v, err := m.genSql.OrderBy("create_time desc").Limit(uint64(pageSize)).Offset(uint64((page - 1) * pageSize)).ToSql()
	if err != nil {
		return nil, err
	}

	resp := make([]*UserDepositFlow, 0)
	err = m.QueryRowsNoCacheCtx(ctx, &resp, q, v...)
	switch err {
	case nil:
		return resp, nil
	default:
		return nil, err
	}
}

func (m *customUserDepositFlowModel) Count(ctx context.Context, uid int64, state []int64, moneyType int64, currencyType int64, payChannelId int64, start, end *time.Time) (int64, error) {
	query := fmt.Sprintf("select count(flow_no) from %s ", m.table)
	m.genSql.InitSql(query)

	if uid != 0 {
		m.genSql = m.genSql.Where("uid = ?", uid)
	}
	if len(state) != 0 {
		if len(state) > 1 {
			m.genSql = m.genSql.Where("state in (?)", state)
		} else {
			m.genSql = m.genSql.Where("state = ?", state[0])
		}
	}
	if moneyType != 0 {
		m.genSql = m.genSql.Where("money_type = ?", moneyType)
	}
	if currencyType != 0 {
		m.genSql = m.genSql.Where("currency_type = ?", currencyType)
	}
	if payChannelId != 0 {
		m.genSql = m.genSql.Where("pay_channel_id = ?", payChannelId)
	} else {
		m.genSql = m.genSql.Where("pay_channel_id not in(?,?)", walletkey.PayChannelIdTest, walletkey.PayChannelIdSystem)
	}
	if start != nil {
		m.genSql = m.genSql.Where("create_time >= ?", start)
	}
	if end != nil {
		m.genSql = m.genSql.Where("create_time <= ?", end)
	}

	q, v, err := m.genSql.ToSql()
	if err != nil {
		return 0, err
	}

	var resp int64
	err = m.QueryRowNoCacheCtx(ctx, &resp, q, v...)
	switch err {
	case nil:
		return resp, nil
	default:
		return 0, err
	}
}

func (m *customUserDepositFlowModel) UpdateState(ctx context.Context, flowNo int64, state int64, exFlow string, errMsg string, gwFlowNo int64) error {
	query := fmt.Sprintf("update %s set state = ?, external_flow_no = ?, err_msg = ?, gw_flow_no = ? where flow_no = ?", m.table)
	_, err := m.ExecNoCacheCtx(ctx, query, state, exFlow, errMsg, gwFlowNo, flowNo)
	if err != nil {
		return err
	}
	return nil
}

func (m *customUserDepositFlowModel) SumAmount(ctx context.Context, payChannelId int64, start, end *time.Time) (int64, error) {
	q := fmt.Sprintf("select coalesce(sum(money_amount), 0) from %s ", m.table)
	m.genSql.InitSql(q)

	if start != nil {
		m.genSql = m.genSql.Where("update_time >= ?", start)
	}
	if end != nil {
		m.genSql = m.genSql.Where("update_time <= ?", end)
	}
	if payChannelId != 0 {
		m.genSql = m.genSql.Where("pay_channel_id = ?", payChannelId)
	} else {
		m.genSql = m.genSql.Where("pay_channel_id not in (?, ?)", walletkey.PayChannelIdTest, walletkey.PayChannelIdSystem)
	}
	m.genSql = m.genSql.Where("state = ?", walletkey.DepositFlowStateSuccess)
	query, args, err := m.genSql.ToSql()
	if err != nil {
		return 0, err
	}
	var resp int64
	err = m.QueryRowNoCacheCtx(ctx, &resp, query, args...)
	switch err {
	case nil:
		return resp, nil
	default:
		return 0, err
	}
}

const countDepositUserSql = "select count(uid) from (select uid from %s where update_time between ? and ? and state = ? and pay_channel_id = ? group by uid having count(uid) >= 1) t"

func (m *customUserDepositFlowModel) CountSuccessUser(ctx context.Context, payChannelId int64, start, end *time.Time) (int64, error) {
	var cnt int64
	var err error
	query := fmt.Sprintf(countDepositUserSql, m.table)
	err = m.QueryRowNoCacheCtx(ctx, &cnt, query, start, end, walletkey.DepositFlowStateSuccess, payChannelId)
	switch err {
	case nil:
		return cnt, nil
	default:
		return 0, err
	}
}

const countDepositUserSql2 = "select count(uid) from (select uid from %s where update_time between ? and ? and state = ? and pay_channel_id != ANY('{1,2}') group by uid having count(uid) >= 1) t"

func (m *customUserDepositFlowModel) CountSuccessUser2(ctx context.Context, start, end *time.Time) (int64, error) {
	var cnt int64
	var err error
	query := fmt.Sprintf(countDepositUserSql2, m.table)
	err = m.QueryRowNoCacheCtx(ctx, &cnt, query, start, end, walletkey.DepositFlowStateSuccess)
	switch err {
	case nil:
		return cnt, nil
	default:
		return 0, err
	}
}

func (m *customUserDepositFlowModel) CountSuccessFlow(ctx context.Context, payChannelId int64, start, end *time.Time) (int64, error) {
	query := fmt.Sprintf("select count(flow_no) from %s ", m.table)
	m.genSql.InitSql(query)

	if start != nil {
		m.genSql = m.genSql.Where("update_time >= ?", start)
	}
	if end != nil {
		m.genSql = m.genSql.Where("update_time <= ?", end)
	}
	if payChannelId != 0 {
		m.genSql = m.genSql.Where("pay_channel_id = ?", payChannelId)
	} else {
		m.genSql = m.genSql.Where("pay_channel_id != ANY(?)", []int64{walletkey.PayChannelIdTest, walletkey.PayChannelIdSystem})
	}
	m.genSql = m.genSql.Where("state = ?", walletkey.DepositFlowStateSuccess)

	q, v, err := m.genSql.ToSql()
	if err != nil {
		return 0, err
	}

	var resp int64
	err = m.QueryRowNoCacheCtx(ctx, &resp, q, v...)
	switch err {
	case nil:
		return resp, nil
	default:
		return 0, err
	}
}

const countDepositNewUserSql = "select count(uid) from (select uid from %s where uid between ? and ? and update_time between ? and ? and state = ? and pay_channel_id != ANY('{1,2}') group by uid having count(uid) >= 1) t"

func (m *customUserDepositFlowModel) CountNewUserCount(ctx context.Context, start, end *time.Time, startUid, endUid int64) (int64, error) {
	var cnt int64
	var err error
	query := fmt.Sprintf(countDepositNewUserSql, m.table)
	err = m.QueryRowNoCacheCtx(ctx, &cnt, query, startUid, endUid, start, end, walletkey.DepositFlowStateSuccess)
	switch err {
	case nil:
		return cnt, nil
	default:
		return 0, err
	}
}

func (m *customUserDepositFlowModel) SumNewUserAmount(ctx context.Context, start, end *time.Time, startUid, endUid int64) (int64, error) {
	query := fmt.Sprintf("select coalesce(sum(money_amount), 0) from %s ", m.table)
	m.genSql.InitSql(query)

	if startUid != 0 {
		m.genSql = m.genSql.Where("uid >= ?", startUid)
	}
	if endUid != 0 {
		m.genSql = m.genSql.Where("uid <= ?", endUid)
	}
	if start != nil {
		m.genSql = m.genSql.Where("update_time >= ?", start)
	}
	if end != nil {
		m.genSql = m.genSql.Where("update_time <= ?", end)
	}
	m.genSql = m.genSql.Where("state = ?", walletkey.DepositFlowStateSuccess).Where("pay_channel_id != ANY(?)", []int64{walletkey.PayChannelIdTest, walletkey.PayChannelIdSystem})
	query, args, err := m.genSql.ToSql()
	if err != nil {
		return 0, err
	}
	var resp int64
	err = m.QueryRowNoCacheCtx(ctx, &resp, query, args...)
	switch err {
	case nil:
		return resp, nil
	default:
		return 0, err
	}
}
